Passed
Push — master ( ea3ca4...674a75 )
by EMP
08:06 queued 06:49
created

main.js ➔ addAddresses   A

Complexity

Conditions 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
"use strict";
2
3
sodium.ready.then(function() {
4
5
let isReady = true;
6
let vaultOk = null;
7
8
const vault = new PostVault(function(ok) {
0 ignored issues
show
Bug introduced by
The variable PostVault seems to be never declared. If this is a global, consider adding a /** global: PostVault */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
9
	if (ok) vaultOk = false;
10
});
11
12
const ae = new AllEars(function(ok) {
13
	if (!ok) {
14
		document.getElementById("greeting").textContent = "Failed loading All-Ears";
15
		return;
16
	}
17
18
	try {
19
		if (localStorage.greeting) {
20
			document.getElementById("greeting").textContent = localStorage.greeting;
21
			document.getElementById("txt_pg").value = localStorage.greeting;
22
		} else localStorage.greeting = document.getElementById("greeting").textContent;
23
	} catch(e) {
24
		document.getElementById("btn_pg").disabled = true;
25
		document.getElementById("txt_pg").disabled = true;
26
		document.getElementById("txt_pg").className = "ita";
27
		document.getElementById("txt_pg").value = "LocalStorage inaccessible";
28
	}
29
30
	document.getElementById("txt_skey").maxLength = "64";
31
});
32
33
function TabState(cur, max, btnDele, btnUpdt) {
34
	this.cur = cur;
35
	this.max = max;
36
	this.btnDele = btnDele;
37
	this.btnUpdt = btnUpdt;
38
}
39
40
const tabs = [
41
	new TabState(0, 0, false, true), // Inbox
42
	new TabState(0, 0, false, false), // Outbx
43
	new TabState(0, 1, true,  false), // Write
44
	new TabState(0, 2, false, false), // Notes
45
	new TabState(0, 2, false, false) // Tools
46
];
47
48
function MsgInfo(msgIdHex, msgType, msgNum) {
49
	this.idHex = msgIdHex;
50
	this.type = msgType;
51
	this.num = msgNum;
52
}
53
54
let msgDisplay = new MsgInfo(null, null, null);
55
let showHeaders = false;
56
let rowsPerPage = 0;
57
58
let tab = 0;
59
const TAB_INBOX = 0;
60
const TAB_DRBOX = 1;
61
const TAB_WRITE = 2;
62
const TAB_NOTES = 3;
63
const TAB_TOOLS = 4;
64
65
function errorDialog(err, focusAfter) {
66
	if (typeof(err) !== "number" || err < 1) return;
67
68
	let btnDisable = [];
69
	const buttons = document.querySelectorAll("nav > button");
70
	buttons.forEach(function(btn) {
71
		btnDisable.push(btn.disabled);
72
		btn.disabled = true;
73
	});
74
75
	const errMsg = ae.getErrorMessage(err);
76
77
	const dlg = document.querySelector("dialog");
78
	dlg.children[0].style.height = getComputedStyle(document.querySelector("#main1 > div[class='mid']")).height;
79
	dlg.querySelector("h1").textContent = "ERROR 0x" + err.toString(16).padStart(2, "0").toUpperCase();
80
	dlg.querySelector("p").textContent = (typeof(errMsg) === "string") ? errMsg : errMsg[1];
81
	dlg.show();
82
83
	document.querySelector("dialog > div").onclick = function() {
84
		buttons.forEach(function(btn, i) {btn.disabled = btnDisable[i];});
85
		dlg.close();
86
		if (focusAfter) focusAfter.focus();
87
	};
88
89
	document.onkeyup = function(event) {
90
		document.onkeyup = null;
91
		event.preventDefault();
92
93
		buttons.forEach(function(btn, i) {btn.disabled = btnDisable[i];});
94
		dlg.close();
95
		if (focusAfter) focusAfter.focus();
96
	};
97
}
98
99
function getCountryFlag(countryCode) {
100
	return (!countryCode || countryCode.length !== 2 || countryCode === "??") ? "❔" : sodium.to_string(new Uint8Array([
101
		240, 159, 135, 166 + countryCode.codePointAt(0) - 65,
102
		240, 159, 135, 166 + countryCode.codePointAt(1) - 65
103
	]));
104
}
105
106
function getClockIcon(d) {
107
	const h24 = d.getUTCHours();
108
	let h12 = (h24 === 0 ? 12 : ((h24 > 12) ? h24 - 12 : h24));
109
110
	const m60 = (d.getUTCMinutes() * 60) + d.getUTCSeconds();
111
	let m30 = 0;
112
	if (m60 <= 900) { // <= 15: round down to this hour
113
		m30 = 0;
114
	} else if (m60 > 900 && m60 < 2700) { // 15..45: round to half-past this hour
115
		m30 = 12;
116
	} else { // >= 45: round up to next hour
117
		h12++;
118
		m30 = 0;
119
	}
120
121
	return String.fromCodePoint((128335 + h12) + m30);
122
}
123
124
function clearDisplay() {
125
	document.getElementById("btn_mnext").disabled = true;
126
	document.getElementById("btn_mprev").disabled = true;
127
	document.getElementById("readmsg_export").hidden = true;
128
129
	const el = document.querySelector("#readmsg_main > img, #readmsg_main > audio, #readmsg_main > video, #readmsg_main > embed, #readmsg_main > iframe");
130
	if (!el) return;
131
	if (el.src) URL.revokeObjectURL(el.src);
132
	el.remove();
133
}
134
135
function clearMsgFlags() {
136
	document.getElementById("readmsg_flags").children[0].replaceChildren();
137
}
138
139
function addMsgFlag(abbr, abbrTitle) {
140
	const parent = document.getElementById("readmsg_flags").children[0];
141
142
	const el = document.createElement("abbr");
143
	el.title = abbrTitle;
144
	el.textContent = abbr;
145
146
	parent.appendChild(document.createTextNode(" "));
147
	parent.appendChild(el);
148
}
149
150
function displayFile(isHistory, num, showNext) {
151
	if (num < 0 || num >= ae.getUplMsgCount()) return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
152
153
	const fileType = ae.getUplMsgType(num);
154
	if (!fileType) {
155
		if (isHistory) return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
156
		if (showNext === true) return displayFile(false, num + 1, true);
157
		if (showNext === false) return displayFile(false, num - 1, false);
158
		ae.downloadUplMsg(num); return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
159
	}
160
161
	clearDisplay();
162
	document.querySelector("article").scroll(0, 0);
163
	document.querySelector("article").setAttribute("data-msgid", ae.getUplMsgIdHex(num));
164
165
	document.getElementById("btn_mdele").disabled = false;
166
	document.getElementById("btn_msave").disabled = false;
167
	document.getElementById("btn_reply").disabled = true;
168
169
	document.getElementById("btn_msave").onclick = function() {ae.downloadUplMsg(num);};
170
171
	document.getElementById("readmsg_info").hidden = true;
172
	document.querySelector("#readmsg_main > h1").textContent = ae.getUplMsgTitle(num);
173
174
	msgDisplay = new MsgInfo(ae.getUplMsgIdHex(num), "upl", num);
175
	if (!isHistory) history.pushState({tab: tab, page: tabs[tab].cur, msg: msgDisplay}, null);
176
177
	document.getElementById("main2").hidden = false;
178
	document.getElementById("main1").hidden = !window.matchMedia("(min-width: 80em)").matches;
179
180
	document.getElementById("btn_mnext").disabled = (num === ae.getUplMsgCount() - 1);
181
	document.getElementById("btn_mprev").disabled = (num === 0);
182
	document.getElementById("btn_mnext").onclick = function() {displayFile(false, num + 1, true);};
183
	document.getElementById("btn_mprev").onclick = function() {displayFile(false, num - 1, false);};
184
185
	if (fileType === "text") {
186
		document.querySelector("#readmsg_main > pre").hidden = false;
187
		try {
188
			document.querySelector("#readmsg_main > pre").textContent = sodium.to_string(ae.getUplMsgBody(num));
189
		} catch(e) {
190
			document.querySelector("#readmsg_main > pre").textContent = "Failed decoding body: " + e.message;
191
		}
192
		return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
193
	}
194
195
	document.querySelector("#readmsg_main > pre").hidden = true;
196
	let el;
197
198
	switch (fileType) {
199
		case "image": {
200
			el = document.createElement("img");
201
			el.src = URL.createObjectURL(new Blob([ae.getUplMsgBody(num).buffer]));
202
			el.onclick = function() {
203
				if (!document.fullscreen)
204
					this.requestFullscreen();
205
				else
206
					document.exitFullscreen();
207
			};
208
		break;}
209
210
		case "audio":
211
		case "video": {
212
			el = document.createElement(fileType);
213
			el.controls = "controls";
214
			el.src = URL.createObjectURL(new Blob([ae.getUplMsgBody(num).buffer]));
215
		break;}
216
217
		case "pdf": {
218
			el = document.createElement("embed");
219
			el.type = "application/pdf";
220
			el.src = URL.createObjectURL(new Blob([ae.getUplMsgBody(num).buffer], {type: "application/pdf"}));
221
		break;}
222
223
		case "html": {
224
			el = document.createElement("iframe");
225
			el.allow = "vertical-scroll";
226
			el.sandbox = "";
227
			el.referrerPolicy = "no-referrer";
228
229
			try {
230
				const sanBody = document.createElement("body");
231
				sanBody.setHTML(sodium.to_string(ae.getUplMsgBody(num).buffer), {sanitizer: new Sanitizer({
0 ignored issues
show
Bug introduced by
The variable Sanitizer seems to be never declared. If this is a global, consider adding a /** global: Sanitizer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
232
					"allowElements": [
233
						"a","div","p",
234
						"h1","h2","h3","h4","h5","h6",
235
						"em","strong","b","i","u"
236
					],
237
					'allowAttributes': {
238
						"href": ["*"]
239
					}
240
				})});
241
242
				el.srcdoc =
243
				"<!doctype html><html>" +
244
					"<head>" +
245
						"<style>" +
246
							"html, body {background: #080a08; color: #fff; scrollbar-color: #222 #333;}\n" +
247
							"body {opacity:0.55;}\n" +
248
							"body > *:first-child {margin-top: 0; padding-top: 0;}\n" +
249
							"a {color: #fff;}\n" +
250
							"button, input, select, textarea {background: #000; color: #fff;}\n" +
251
						"</style>" +
252
					"</head>" +
253
					sanBody.outerHTML +
254
				"</html>";
255
			} catch(e) {
256
				el.srcdoc = "<!doctype html><html><head><style>body {background: #080a08; color: #fff; opacity:0.55;} h1 {margin: 0;}</style><body><h1>Error</h1><p>" + e.message + "</p></body></html>";
257
			}
258
		break;}
259
260
		case "svg": {
261
			el = document.createElement("iframe");
262
			el.allow = "";
263
			el.sandbox = "";
264
			el.referrerPolicy = "no-referrer";
265
			el.srcdoc = "<!doctype><html><head><style>body,html,svg {margin: 0; padding: 0; border: 0; height: 100%; width: 100%; display: block; background: #080a08;}</style></head><body>" + sodium.to_string(ae.getUplMsgBody(num).buffer) + "</body></html>";
266
		break;}
267
268
		default: return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
269
	}
270
271
	document.getElementById("readmsg_main").appendChild(el);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
272
}
273
274
function displayExport(isHistory, isInt, num) {
275
	clearDisplay();
276
	document.getElementById("readmsg_main").hidden = true;
277
	document.getElementById("readmsg_export").hidden = false;
278
	document.getElementById("btn_msave").blur();
279
	document.getElementById("btn_msave").disabled = true;
280
	document.getElementById("btn_reply").disabled = true;
281
	document.getElementById("btn_mdele").disabled = true;
282
283
//	document.querySelector("#readmsg_export > div:nth-child(1)").onclick = function() {};
284
	document.querySelector("#readmsg_export > div:nth-child(2)").onclick = function() {if (isInt) {ae.downloadIntMsg(num);} else {ae.downloadExtMsg(num);} displayMsg(false, isInt, num);};
285
	document.querySelector("#readmsg_export > div:nth-child(3)").onclick = function() {if (isInt) {ae.htmlIntMsg(num, true);} else {ae.htmlExtMsg(num, true);} displayMsg(false, isInt, num);};
286
	document.querySelector("#readmsg_export > div:nth-child(4)").onclick = function() {if (isInt) {ae.txtIntMsg(num, true);} else {ae.txtExtMsg(num, true);} displayMsg(false, isInt, num);};
287
	document.querySelector("#readmsg_export > div:nth-child(5)").onclick = function() {if (isInt) {ae.printIntMsg(num);} else {ae.printExtMsg(num);} displayMsg(false, isInt, num);};
288
	document.querySelector("#readmsg_export > div:nth-child(6)").onclick = function() {navigator.clipboard.writeText(isInt? ae.txtIntMsg(num, false) : ae.txtExtMsg(num, false)); displayMsg(false, isInt, num);};
289
290
	msgDisplay = new MsgInfo(isInt? ae.getIntMsgIdHex(num) : ae.getExtMsgIdHex(num), isInt? "int_exp" : "ext_exp", num);
291
	if (!isHistory) history.pushState({tab: tab, page: tabs[tab].cur, msg: msgDisplay}, null);
292
}
293
294
function displayMsg(isHistory, isInt, num) {
295
	clearDisplay();
296
	document.getElementById("btn_mdele").disabled = false;
297
298
	document.querySelector("article").scroll(0, 0);
299
	document.querySelector("article").setAttribute("data-msgid", isInt? ae.getIntMsgIdHex(num) : ae.getExtMsgIdHex(num));
300
301
	document.getElementById("btn_msave").disabled = false;
302
	document.getElementById("btn_msave").onclick = function() {displayExport(false, isInt, num);};
303
304
	const ts = isInt? ae.getIntMsgTime(num) : ae.getExtMsgTime(num);
305
306
	if (!isInt || (ae.getIntMsgFrom(num) !== "public" && ae.getIntMsgFrom(num) !== "system")) {
307
		document.getElementById("btn_reply").disabled = false;
308
309
		document.getElementById("btn_reply").onclick = function() {
310
			document.getElementById("write_recv").value = isInt? ae.getIntMsgFrom(num) : ae.getExtMsgReplyAddress(num);
311
			document.getElementById("write_subj").value = isInt? ae.getIntMsgTitle(num) : ae.getExtMsgTitle(num);
312
			if (!document.getElementById("write_subj").value.startsWith("Re:")) document.getElementById("write_subj").value = "Re: " + document.getElementById("write_subj").value;
313
			document.querySelector("#write2_pkey > input").value = isInt? ae.getIntMsgApk(num) : "";
314
315
			document.getElementById("write_recv").readOnly = !isInt;
316
			document.getElementById("write_subj").readOnly = !isInt;
317
			document.getElementById("write_subj").setAttribute("data-replyid", isInt? "" : ae.getExtMsgHdrId(num));
318
319
			tabs[TAB_WRITE].cur = 0;
320
			document.getElementById("btn_write").disabled = false;
321
			document.getElementById("btn_write").click();
322
			document.getElementById("write_body").focus();
323
324
			for (const opt of document.getElementById("write_from").options) {
325
				if (opt.value === (isInt ? ae.getIntMsgTo(num) : ae.getExtMsgEnvTo(num).split("@")[0].toLowerCase())) {
326
					opt.selected = true;
327
				}
328
			}
329
		};
330
	} else {
331
		document.getElementById("btn_reply").disabled = true;
332
	}
333
334
	document.getElementById("readmsg_info").hidden = false;
335
	document.querySelector("#readmsg_main > pre").hidden = false;
336
337
	document.getElementById("readmsg_envto").textContent = isInt ? "" : ae.getExtMsgEnvTo(num);
338
	document.getElementById("readmsg_hdrto").textContent = isInt ? ae.getIntMsgTo(num) : (ae.getExtMsgHdrTo(num));
339
	if(!isInt && ae.getExtMsgDnTo(num)) {
340
		const span = document.createElement("span");
341
		span.className = "sans";
342
		span.textContent = " • " + ae.getExtMsgDnTo(num);
343
		document.getElementById("readmsg_hdrto").appendChild(span);
344
	}
345
346
	const tzOs = new Date().getTimezoneOffset();
347
	const msgDate = new Date((ts * 1000) + (tzOs * -60000));
348
	document.getElementById("readmsg_date").children[0].textContent = getClockIcon(msgDate);
349
	document.getElementById("readmsg_date").children[1].dateTime = new Date(ts * 1000).toISOString();
350
351
	if (isInt) {
352
		document.querySelector("#readmsg_main > h1").textContent = ae.getIntMsgTitle(num);
353
		document.querySelector("#readmsg_main > pre").textContent = ae.getIntMsgBody(num);
354
355
		document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ");
356
357
		document.getElementById("readmsg_ip").style.visibility = "hidden";
358
		document.getElementById("readmsg_rdns").style.visibility = "hidden";
359
		document.getElementById("readmsg_dkim").style.visibility = "hidden";
360
		document.getElementById("readmsg_greet").style.visibility = "hidden";
361
		document.getElementById("readmsg_cert").style.visibility = "hidden";
362
		document.getElementById("readmsg_envfrom").style.visibility = "hidden";
363
		document.getElementById("readmsg_envto").style.visibility = "hidden";
364
365
		if (ae.getIntMsgFrom(num) !== "system" && ae.getIntMsgFrom(num) !== "public") {
366
			document.getElementById("readmsg_tls").style.visibility = "visible";
367
			document.getElementById("readmsg_tls").children[0].textContent = ae.getIntMsgApk(num);
368
		} else document.getElementById("readmsg_tls").style.visibility = "hidden";
369
370
		let symbol = document.createElement("span");
371
		if      (ae.getIntMsgLevel(num) === 3 && ae.getIntMsgFrom(num) === "system") {symbol.title = "System message"; symbol.textContent = "🅢";}
372
		else if (ae.getIntMsgLevel(num) === 3 && ae.getIntMsgFrom(num) === "public") {symbol.title = "Public announcement"; symbol.textContent = "🅟";}
373
		else if (ae.getIntMsgLevel(num) === 3) {symbol.title = "Administrator"; symbol.textContent = "🅐";}
374
		else if (ae.getIntMsgLevel(num) === 2) {symbol.title = "Level 2";  symbol.textContent = "➋";}
375
		else if (ae.getIntMsgLevel(num) === 1) {symbol.title = "Level 1";  symbol.textContent = "➊";}
376
		else if (ae.getIntMsgLevel(num) === 0) {symbol.title = "Level 0";  symbol.textContent = "🄌";}
377
		else {symbol.title = "Invalid level"; symbol.textContent = "⚠";}
378
379
		document.getElementById("readmsg_hdrfrom").replaceChildren(symbol, document.createTextNode(" " + ae.getIntMsgFrom(num)));
380
381
		clearMsgFlags();
382
		if (!ae.getIntMsgFlagVPad(num)) addMsgFlag("PAD", "Invalid padding");
383
		if (!ae.getIntMsgFlagVSig(num)) addMsgFlag("SIG", "Invalid signature");
384
		if ( ae.getIntMsgFlagE2ee(num)) addMsgFlag("E2EE", "End-to-end encrypted");
385
	} else {
386
		const headers = document.createElement("p");
387
		headers.textContent = ae.getExtMsgHeaders(num);
388
		headers.className = "mono";
389
		headers.hidden = !showHeaders;
390
391
		const body = document.createElement("p");
392
		body.innerHTML = ae.getExtMsgBody(num, false);
393
394
		document.querySelector("#readmsg_main > pre").replaceChildren(headers, body);
395
396
		const h1 = document.querySelector("#readmsg_main > h1");
397
		h1.textContent = ae.getExtMsgTitle(num);
398
		h1.style.cursor = headers.textContent? "pointer" : "";
399
		h1.onclick = function() {
400
			if (!headers.textContent) return;
401
			showHeaders = !showHeaders;
402
			headers.hidden = !showHeaders;
403
		};
404
405
		let hdrSecs = Math.abs(ae.getExtMsgHdrTime(num));
406
		let hdrTime = "";
407
		if (hdrSecs >= 3600) {
408
			const hdrHours = Math.floor(hdrSecs / 3600);
409
			hdrTime += hdrHours.toString() + "h ";
410
			hdrSecs -= hdrHours * 3600;
411
		}
412
		if (hdrSecs >= 60) {
413
			const hdrMins = Math.floor(hdrSecs / 60);
414
			hdrTime += hdrMins.toString() + "m ";
415
			hdrSecs -= hdrMins * 60;
416
		}
417
		hdrTime += hdrSecs + "s";
418
419
		const hdrTz = (ae.getExtMsgHdrTz(num) >= 0 ? "+" : "-") + Math.floor(Math.abs(ae.getExtMsgHdrTz(num)) / 60).toString().padStart(2, "0") + (Math.abs(ae.getExtMsgHdrTz(num)) % 60).toString().padStart(2, "0");
420
421
		let spans = [document.createElement("span"), document.createElement("span"), document.createElement("span")];
422
		spans[0].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ");
423
		spans[1].className = "sans";
424
		spans[1].textContent = " • ";
425
		spans[2].textContent = hdrTz + " " + ((ae.getExtMsgHdrTime(num) >= 0) ? "+" : "-") + hdrTime;
426
		document.getElementById("readmsg_date").children[1].replaceChildren(...spans);
427
428
		document.getElementById("readmsg_ip").style.visibility = "visible";
429
		document.getElementById("readmsg_rdns").style.visibility = "visible";
430
		document.getElementById("readmsg_dkim").style.visibility = "visible";
431
		document.getElementById("readmsg_greet").style.visibility = "visible";
432
		document.getElementById("readmsg_tls").style.visibility = "visible";
433
		document.getElementById("readmsg_cert").style.visibility = "visible";
434
		document.getElementById("readmsg_envfrom").style.visibility = "visible";
435
		document.getElementById("readmsg_envto").style.visibility = "visible";
436
437
		// DKIM
438
		let dkim = "";
439
		if (ae.getExtMsgDkim(num)) {
440
			[ // Look for a matching domain in this order
441
				ae.getExtMsgHdrFrom(num).split("@")[1],
442
				ae.getExtMsgEnvFrom(num).split("@")[1],
443
				ae.getExtMsgRdns(num),
444
				ae.getExtMsgGreet(num),
445
				ae.getExtMsgTlsDomain(num)
446
			].forEach(function(dom) {
447
				if (dkim) return;
448
				for (let i = 0; i < ae.getExtMsgDkim(num).domain.length; i++) {
449
					if (ae.getExtMsgDkim(num).domain[i] === dom) {
450
						dkim = dom + " ✓";
451
						return;
452
					}
453
				}
454
			});
455
456
			if (!dkim) dkim = ae.getExtMsgDkim(num).domain[0]; // Default to first signature domain
457
		}
458
459
		if (ae.getExtMsgFlagDkFl(num)) dkim += " (fail)";
460
461
		// Left side
462
		document.getElementById("readmsg_country").textContent = getCountryFlag(ae.getExtMsgCcode(num));
463
		document.getElementById("readmsg_country").title = ae.getExtMsgCname(num);
464
		document.getElementById("readmsg_ip").children[1].textContent = ae.getExtMsgIp(num) + (ae.getExtMsgFlagIpBl(num) ? " ❗" : "");
465
		document.getElementById("readmsg_ip").children[2].textContent = " • " + ae.getExtMsgAuSys(num);
466
		document.getElementById("readmsg_tls").children[0].textContent = ae.getExtMsgTLS(num);
467
468
		// Right side
469
		document.getElementById("readmsg_greet").children[0].textContent = ae.getExtMsgGreet(num) + (ae.getExtMsgFlagGrDm(num) ? " ✓" : "");
470
		document.getElementById("readmsg_rdns").children[0].textContent = ae.getExtMsgRdns(num) + (ae.getExtMsgGreet(num).toLowerCase() === ae.getExtMsgRdns(num).toLowerCase() ? " ✓" : "");
471
		document.getElementById("readmsg_cert").children[0].textContent = ae.getExtMsgTlsDomain(num) ? (ae.getExtMsgTlsDomain(num) + " ✓") : "";
472
		document.getElementById("readmsg_dkim").children[0].textContent = dkim;
473
		document.getElementById("readmsg_envfrom").textContent = ae.getExtMsgEnvFrom(num);
474
		document.getElementById("readmsg_hdrfrom").textContent = ae.getExtMsgHdrFrom(num);
475
		if (ae.getExtMsgDnFrom(num)) {
476
			const span = document.createElement("span");
477
			span.className = "sans";
478
			span.textContent = " • " + ae.getExtMsgDnFrom(num);
479
			document.getElementById("readmsg_hdrfrom").appendChild(span);
480
		}
481
482
		clearMsgFlags();
483
		if (!ae.getExtMsgFlagVPad(num)) addMsgFlag("PAD", "Invalid padding");
484
		if (!ae.getExtMsgFlagVSig(num)) addMsgFlag("SIG", "Invalid signature");
485
		if (!ae.getExtMsgFlagPExt(num)) addMsgFlag("SMTP", "The sender did not use the Extended (ESMTP) protocol");
486
		if ( ae.getExtMsgFlagRare(num)) addMsgFlag("RARE", "The sender issued unusual command(s)");
487
		if ( ae.getExtMsgFlagFail(num)) addMsgFlag("FAIL", "The sender issued invalid command(s)");
488
		if ( ae.getExtMsgFlagPErr(num)) addMsgFlag("PROT", "The sender violated the protocol");
489
	}
490
491
	document.getElementById("readmsg_main").hidden = false;
492
	document.getElementById("main2").hidden = false;
493
	document.getElementById("main1").hidden = !window.matchMedia("(min-width: 80em)").matches;
494
495
	msgDisplay = new MsgInfo(isInt? ae.getIntMsgIdHex(num) : ae.getExtMsgIdHex(num), isInt? "int" : "ext", num);
496
	if (!isHistory) history.pushState({tab: tab, page: tabs[tab].cur, msg: msgDisplay}, null);
497
}
498
499
function displayOutMsg(isHistory, num) {
500
	clearDisplay();
501
	document.querySelector("article").scroll(0, 0);
502
	document.querySelector("article").setAttribute("data-msgid", ae.getOutMsgIdHex(num));
503
504
	document.getElementById("btn_mdele").disabled = false;
505
	document.getElementById("btn_msave").disabled = true;
506
	document.getElementById("btn_reply").disabled = true;
507
508
	document.getElementById("readmsg_info").hidden = false;
509
	document.querySelector("#readmsg_main > pre").hidden = false;
510
511
	document.querySelector("#readmsg_main > h1").textContent = ae.getOutMsgSubj(num);
512
	document.querySelector("#readmsg_main > pre").textContent = ae.getOutMsgBody(num);
513
514
	document.getElementById("readmsg_dkim").style.visibility    = "hidden";
515
	document.getElementById("readmsg_hdrto").style.visibility   = "visible";
516
	document.getElementById("readmsg_hdrfrom").style.visibility = "visible";
517
	document.getElementById("readmsg_envto").style.visibility   = "visible";
518
	document.getElementById("readmsg_envfrom").style.visibility = "hidden";
519
520
	document.getElementById("readmsg_hdrfrom").textContent = ae.getOutMsgFrom(num);
521
522
	document.getElementById("readmsg_envto").textContent = ae.getOutMsgMxDom(num);
523
	document.getElementById("readmsg_hdrto").textContent = ae.getOutMsgTo(num);
524
525
	const ts = ae.getOutMsgTime(num);
526
	const tzOs = new Date().getTimezoneOffset();
527
	document.getElementById("readmsg_date").children[1].textContent = new Date((ts * 1000) + (tzOs * -60000)).toISOString().slice(0, 19).replace("T", " ");
528
529
	const isInt = ae.getOutMsgIsInt(num);
530
	document.getElementById("readmsg_ip").style.visibility    = isInt? "hidden" : "visible";
531
	document.getElementById("readmsg_rdns").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
532
	document.getElementById("readmsg_tls").style.visibility   = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
533
	document.getElementById("readmsg_cert").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
534
	document.getElementById("readmsg_greet").style.visibility = isInt? "hidden" : "visible";
535
536
	if (!isInt) {
537
		document.getElementById("readmsg_ip").children[1].textContent = ae.getOutMsgIp(num);
538
		document.getElementById("readmsg_country").textContent = getCountryFlag(ae.getOutMsgCcode(num));
539
		document.getElementById("readmsg_country").title = ae.getOutMsgCname(num);
540
//		document.getElementById("readmsg_tls").children[0].textContent = ae.getOutMsgTLS(num);
541
		document.getElementById("readmsg_greet").children[0].textContent = ae.getOutMsgGreet(num);
542
	}
543
544
	clearMsgFlags();
545
	if (!ae.getOutMsgFlagVPad(num)) addMsgFlag("PAD", "Invalid padding");
546
	if (!ae.getOutMsgFlagVSig(num)) addMsgFlag("SIG", "Invalid signature");
547
	if ( ae.getOutMsgFlagE2ee(num)) addMsgFlag("E2EE", "End-to-end encrypted");
548
549
	document.getElementById("main2").hidden = false;
550
	document.getElementById("main1").hidden = !window.matchMedia("(min-width: 80em)").matches;
551
552
	msgDisplay = new MsgInfo(ae.getOutMsgIdHex(num), "out", num);
553
	if (!isHistory) history.pushState({tab: tab, page: tabs[tab].cur, msg: msgDisplay}, null);
554
}
555
556
function updateAddressButtons() {
557
	const limitReached = (ae.getAddressCountNormal() + ae.getAddressCountShield() >= 31);
558
	document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.getAddressCountNormal() >= ae.getLimitNormalA(ae.getOwnLevel()));
559
	document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.getAddressCountShield() >= ae.getLimitShieldA(ae.getOwnLevel()));
560
}
561
562
function updateAddressCounts() {
563
	document.querySelector("#tbd_accs > tr > td:nth-child(3)").textContent = ae.getAddressCountNormal();
564
	document.querySelector("#tbd_accs > tr > td:nth-child(4)").textContent = ae.getAddressCountShield();
565
566
	document.getElementById("limit_normal").textContent = (ae.getAddressCountNormal() + "/" + ae.getLimitNormalA(ae.getOwnLevel())).padStart(ae.getLimitNormalA(ae.getOwnLevel()) > 9 ? 5 : 1);
567
	document.getElementById("limit_shield").textContent = (ae.getAddressCountShield() + "/" + ae.getLimitShieldA(ae.getOwnLevel())).padStart(ae.getLimitShieldA(ae.getOwnLevel()) > 9 ? 5 : 1);
568
	document.getElementById("limit_total").textContent = ((ae.getAddressCountNormal() + ae.getAddressCountShield()) + "/" + ae.getAddrPerUser()).padStart(5);
569
570
	updateAddressButtons();
571
}
572
573
function addOwnAccount() {
574
	const row = document.getElementById("tbd_accs").insertRow(-1);
575
576
	let cell;
577
	cell = row.insertCell(-1); cell.textContent = ae.getOwnUpk();
578
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.getTotalMsgBytes() / 1048576); // MiB
579
	cell = row.insertCell(-1); cell.textContent = ae.getAddressCountNormal();
580
	cell = row.insertCell(-1); cell.textContent = ae.getAddressCountShield();
581
582
	cell = row.insertCell(-1);
583
	let btn = document.createElement("button");
584
	btn.type = "button";
585
	btn.textContent = "+";
586
	btn.disabled = true;
587
	cell.appendChild(btn);
588
589
	cell = row.insertCell(-1); cell.textContent = ae.getOwnLevel();
590
591
	cell = row.insertCell(-1);
592
	btn = document.createElement("button");
593
	btn.type = "button";
594
	btn.textContent = "−";
595
	btn.disabled = true;
596
	btn.id = "btn_lowme";
597
	btn.onclick = function() {
598
		const newLevel = parseInt(row.cells[5].textContent, 10) - 1;
599
		ae.Account_Update(ae.getOwnUpk(), newLevel, function(error) {
600
			if (error === 0) {
601
				row.cells[5].textContent = newLevel;
602
				if (newLevel === 0) {document.getElementById("btn_lowme").disabled = true;}
603
			} else errorDialog(error);
604
		});
605
	};
606
	cell.appendChild(btn);
607
608
	cell = row.insertCell(-1);
609
	btn = document.createElement("button");
610
	btn.type = "button";
611
	btn.textContent = "X";
612
	btn.disabled = true;
613
	btn.id = "btn_delme";
614
	btn.onclick = function() {
615
		ae.Account_Delete(ae.getOwnUpk(), function(error) {
616
			if (error === 0) {
617
				row.remove();
618
				document.getElementById("fs_users").disabled = true;
619
			} else errorDialog(error);
620
		});
621
	};
622
	cell.appendChild(btn);
623
}
624
625
function adjustLevel(pubkey, level, c) {
626
	const fs = document.getElementById("tbl_accs");
627
	fs.disabled = true;
628
629
	ae.Account_Update(pubkey, level, function(error) {
630
		fs.disabled = false;
631
632
		if (error === 0) {
633
			c[4].children[0].disabled = (level === 3);
634
			c[5].textContent = level;
635
			c[6].children[0].disabled = (level === 0);
636
		} else errorDialog(error);
637
	});
638
}
639
640
function addMsg(isInt, i) {
641
	const row = document.getElementById("tbl_inbox").insertRow(-1);
642
	row.setAttribute("data-msgid", isInt? ae.getIntMsgIdHex(i) : ae.getExtMsgIdHex(i));
643
644
	const ts = isInt? ae.getIntMsgTime(i) : ae.getExtMsgTime(i);
645
	const el = document.createElement("time");
646
	el.dateTime = new Date(ts * 1000).toISOString();
647
	el.textContent = new Date((ts * 1000) + (new Date().getTimezoneOffset() * -60000)).toISOString().slice(0, 10);
648
	let cell = row.insertCell(-1);
649
	cell.appendChild(el);
650
651
	cell = row.insertCell(-1);
652
	cell.textContent = isInt? ae.getIntMsgTitle(i) : ae.getExtMsgTitle(i);
653
	if (!cell.textContent) cell.textContent = "(fail)";
654
655
	if (isInt) {
656
		cell = row.insertCell(-1);
657
		cell.textContent = ae.getIntMsgFrom(i);
658
		cell.className = (ae.getIntMsgFrom(i).length === 16) ? "mono" : "";
659
	} else {
660
		let from1 = ae.getExtMsgHdrFrom(i);
661
		if (!from1) from1 = ae.getExtMsgEnvFrom(i);
662
		const from2 = from1.substring(from1.indexOf("@") + 1);
663
		cell = row.insertCell(-1);
664
		cell.textContent = from1.substring(0, from1.indexOf("@"));
665
666
		const flag = document.createElement("abbr");
667
		flag.textContent = getCountryFlag(ae.getExtMsgCcode(i));
668
		flag.title = ae.getExtMsgCname(i);
669
670
		const fromText = document.createElement("span");
671
		fromText.textContent = " " + from2;
672
673
		cell = row.insertCell(-1);
674
		cell.appendChild(flag);
675
		cell.appendChild(fromText);
676
	}
677
678
	row.onclick = function() {
679
		displayMsg(false, isInt, i);
680
	};
681
}
682
683
function setRowsPerPage(tbl) {
684
	tbl.replaceChildren();
685
	const row = tbl.insertRow(-1);
686
	const cell = row.insertCell(-1);
687
	cell.textContent = "0";
688
	rowsPerPage = Math.floor(getComputedStyle(tbl).height.replace("px", "") / getComputedStyle(tbl.getElementsByTagName("tr")[0]).height.replace("px", ""));
689
	tbl.replaceChildren();
690
}
691
692
function showInbox() {
693
	const tbl = document.getElementById("tbl_inbox");
694
	if (!document.getElementById("main1").hidden) setRowsPerPage(tbl);
695
696
	const maxExt = ae.getExtMsgCount();
697
	const maxInt = ae.getIntMsgCount();
698
	const loadMore = ae.getReadyMsgBytes() < ae.getTotalMsgBytes();
699
700
	if (maxExt + maxInt > 0) {
701
		tabs[TAB_INBOX].max = Math.floor((maxExt + maxInt - (loadMore? 0 : 1)) / rowsPerPage);
702
		document.getElementById("btn_rght").disabled = (tabs[TAB_INBOX].cur >= tabs[TAB_INBOX].max);
703
		tbl.replaceChildren();
704
705
		let skipMsgs = rowsPerPage * tabs[TAB_INBOX].cur;
706
		let numExt = 0;
707
		let numInt = 0;
708
		let numAdd = 0;
709
710
		while (numAdd < rowsPerPage) {
711
			const tsInt = (numInt < maxInt) ? ae.getIntMsgTime(numInt) : -1;
712
			const tsExt = (numExt < maxExt) ? ae.getExtMsgTime(numExt) : -1;
713
			if (tsInt === -1 && tsExt === -1) break;
714
715
			if (tsInt !== -1 && (tsExt === -1 || tsInt > tsExt)) {
716
				if (skipMsgs > 0) skipMsgs--; else {addMsg(true, numInt); numAdd++;}
717
				numInt++;
718
			} else if (tsExt !== -1) {
719
				if (skipMsgs > 0) skipMsgs--; else {addMsg(false, numExt); numAdd++;}
720
				numExt++;
721
			}
722
		}
723
	} else {
724
		tabs[TAB_INBOX].max = 0;
725
	}
726
727
	if (loadMore && tabs[TAB_INBOX].cur >= tabs[TAB_INBOX].max) {
728
		const row = tbl.insertRow(-1);
729
		const cell = row.insertCell(-1);
730
		cell.textContent = "Load more (" + Math.round((ae.getTotalMsgBytes() - ae.getReadyMsgBytes()) / 1024) + " KiB left)";
731
732
		row.onclick = function() {
733
			tbl.style.opacity = 0.5;
734
735
			ae.Message_Browse(false, false, function(errorBrowse) {
736
				tbl.style.opacity = 1;
737
738
				if (errorBrowse !== 0) {
739
					errorDialog(errorBrowse);
740
					return;
741
				}
742
743
				showInbox();
744
			});
745
		};
746
	}
747
}
748
749
function showDrbox() {
750
	const tbl = document.getElementById("tbl_drbox");
751
	if (!document.getElementById("main1").hidden) setRowsPerPage(tbl);
752
753
	const drCount = ae.getOutMsgCount();
754
	const loadMore = ae.getReadyMsgBytes() < ae.getTotalMsgBytes();
755
756
	if (drCount > 0) {
757
		tabs[TAB_DRBOX].max = Math.floor((drCount - (loadMore? 0 : 1)) / rowsPerPage);
758
		document.getElementById("btn_rght").disabled = (tabs[TAB_DRBOX].cur >= tabs[TAB_DRBOX].max);
759
		tbl.replaceChildren();
760
761
		let skipMsgs = rowsPerPage * tabs[TAB_DRBOX].cur;
762
		let numAdd = 0;
763
764
		for (let i = 0; numAdd < rowsPerPage && i < drCount; i++) {
765
			if (skipMsgs > 0) {
766
				skipMsgs--;
767
				continue;
768
			}
769
770
			const row = tbl.insertRow(-1);
771
			row.setAttribute("data-msgid", ae.getOutMsgIdHex(i));
772
773
			let cell;
774
			cell = row.insertCell(-1); cell.textContent = new Date(ae.getOutMsgTime(i) * 1000).toISOString().slice(0, 10);
775
			cell = row.insertCell(-1); cell.textContent = ae.getOutMsgSubj(i);
776
			row.onclick = function() {displayOutMsg(false, i);};
777
778
			numAdd++;
779
		}
780
	} else {
781
		tabs[TAB_DRBOX].max = 0;
782
	}
783
784
	if (loadMore && tabs[TAB_DRBOX].cur >= tabs[TAB_DRBOX].max) {
785
		const row = tbl.insertRow(-1);
786
		const cell = row.insertCell(-1);
787
		cell.textContent = "Load more (" + Math.round((ae.getTotalMsgBytes() - ae.getReadyMsgBytes()) / 1024) + " KiB left)";
788
789
		row.onclick = function() {
790
			tbl.style.opacity = 0.5;
791
792
			ae.Message_Browse(false, false, function(errorBrowse) {
793
				tbl.style.opacity = 1;
794
795
				if (errorBrowse !== 0) {
796
					errorDialog(errorBrowse);
797
					return;
798
				}
799
800
				showDrbox();
801
			});
802
		};
803
	}
804
}
805
806
function showFiles() {
807
	const tbl = document.getElementById("tbl_files");
808
	if (!document.getElementById("main1").hidden) setRowsPerPage(tbl);
809
810
	const msgCount = ae.getUplMsgCount() + (vaultOk? vault.getFileCount() : 0);
811
	const loadMore = (ae.getReadyMsgBytes() < ae.getTotalMsgBytes()) || (vaultOk === false);
812
813
	if (msgCount > 0) {
814
		tabs[TAB_NOTES].max = 2 + Math.floor((msgCount - (loadMore? 0 : 1)) / rowsPerPage);
815
		document.getElementById("btn_rght").disabled = (tabs[TAB_NOTES].cur >= tabs[TAB_NOTES].max);
816
		tbl.replaceChildren();
817
818
		let skipMsgs = rowsPerPage * (tabs[TAB_NOTES].cur - 2);
819
		let numAdd = 0;
820
821
		for (let i = 0; numAdd < rowsPerPage && i < ae.getUplMsgCount(); i++) {
822
			if (skipMsgs > 0) {
823
				skipMsgs--;
824
				continue;
825
			}
826
827
			const row = tbl.insertRow(-1);
828
			row.setAttribute("data-msgid", ae.getUplMsgIdHex(i));
829
			row.className = "rowfile";
830
831
			let cell = row.insertCell(-1);
832
			cell.textContent = new Date(ae.getUplMsgTime(i) * 1000).toISOString().slice(0, 10);
833
			cell.onclick = function() {displayFile(false, i, null);};
834
835
			cell = row.insertCell(-1);
836
			cell.textContent = (ae.getUplMsgBytes(i) / 1024).toFixed(0).padStart(4, " ");
837
			cell.onclick = function() {displayFile(false, i, null);};
838
839
			cell = row.insertCell(-1);
840
			const parentNum = ae.getUplMsgParent(i);
841
			if (typeof(parentNum) === "number") {
842
				cell.textContent = ae.getExtMsgTitle(parentNum);
843
				cell.onclick = function() {displayMsg(false, false, parentNum);};
844
			} else if (parentNum === false) {
845
				cell.textContent = "Upload";
846
			} else {
847
				cell.textContent = "Unknown";
848
			}
849
850
			cell = row.insertCell(-1);
851
			cell.textContent = ae.getUplMsgTitle(i);
852
			cell.onclick = function() {displayFile(false, i, null);};
853
854
			cell = row.insertCell(-1);
855
			const btn = document.createElement("button");
856
			btn.setAttribute("data-msgid", ae.getUplMsgIdHex(i));
857
			btn.type = "button";
858
			btn.textContent = "X";
859
			btn.onclick = function() {
860
				ae.Message_Delete(this.getAttribute("data-msgid"), function(error) {
861
					if (error === 0) showFiles();
862
					else errorDialog(error);
863
				});
864
			};
865
			cell.appendChild(btn);
866
867
			numAdd++;
868
		}
869
870
		if (vaultOk) {
871
			for (let i = 0; numAdd < rowsPerPage && i < 256; i++) {
872
				if (vault.getFileSize(i) < 1) continue;
873
874
				if (skipMsgs > 0) {
875
					skipMsgs--;
876
					continue;
877
				}
878
879
				const row = tbl.insertRow(-1);
880
				row.setAttribute("data-msgid", i);
881
				row.className = "rowfile";
882
883
				let cell = row.insertCell(-1);
884
				cell.textContent = new Date(vault.getFileTime(i) * 1000).toISOString().slice(0, 10);
885
886
				cell = row.insertCell(-1);
887
				cell.textContent = (vault.getFileSize(i) / 1024).toFixed(0).padStart(4, " ");
888
889
				cell = row.insertCell(-1);
890
				cell.textContent = "Vault";
891
892
				cell = row.insertCell(-1);
893
				cell.textContent = vault.getFileName(i);
894
				cell.onclick = function() {vault.downloadFile(i);};
895
896
				cell = row.insertCell(-1);
897
				const btn = document.createElement("button");
898
				btn.type = "button";
899
				btn.textContent = "X";
900
				btn.onclick = function() {
901
					vault.deleteFile(i, function(error) {
902
						if (error === 0) showFiles();
903
						else errorDialog(error);
904
					});
905
				};
906
				cell.appendChild(btn);
907
908
				numAdd++;
909
			}
910
		}
911
	} else {
912
		tabs[TAB_NOTES].max = (vaultOk === false) ? 3 : 2;
913
	}
914
915
	if (loadMore && tabs[TAB_NOTES].cur >= tabs[TAB_NOTES].max) {
916
		const row = tbl.insertRow(-1);
917
		row.className = "rowfilex";
918
919
		let cell = row.insertCell(-1);
920
		if (ae.getReadyMsgBytes() < ae.getTotalMsgBytes()) {
921
			cell.textContent = "Load more (" + Math.round((ae.getTotalMsgBytes() - ae.getReadyMsgBytes()) / 1024) + " KiB left)";
922
			cell.onclick = function() {
923
				tbl.style.opacity = 0.5;
924
925
				ae.Message_Browse(false, false, function(errorBrowse) {
926
					tbl.style.opacity = 1;
927
928
					if (errorBrowse !== 0) {
929
						errorDialog(errorBrowse);
930
						return;
931
					}
932
933
					showFiles();
934
				});
935
			};
936
		}
937
938
		cell = row.insertCell(-1);
939
		if (vaultOk === false) {
940
			cell.textContent = "Open PostVault";
941
			cell.onclick = function() {
942
				tbl.style.opacity = 0.5;
943
944
				vault.getInfo(function() {
945
					tbl.style.opacity = 1;
946
947
					// TODO check for error
948
949
					vaultOk = true;
950
					showFiles();
951
				});
952
			};
953
		}
954
	}
955
}
956
957
function addAccountToTable(i) {
958
	const row = document.getElementById("tbd_accs").insertRow(-1);
959
	let cell;
960
	cell = row.insertCell(-1); cell.textContent = ae.admin_getUserUpk(i);
961
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.admin_getUserKib(i) / 1024);
962
	cell = row.insertCell(-1); cell.textContent = ae.admin_getUserNrm(i);
963
	cell = row.insertCell(-1); cell.textContent = ae.admin_getUserShd(i);
964
965
	cell = row.insertCell(-1);
966
	let btn = document.createElement("button");
967
	btn.type = "button";
968
	btn.textContent = "+";
969
	btn.disabled = (ae.admin_getUserLvl(i) === 3);
970
	btn.onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[5].textContent, 10) + 1, c);};
971
	cell.appendChild(btn);
972
973
	cell = row.insertCell(-1); cell.textContent = ae.admin_getUserLvl(i);
974
975
	cell = row.insertCell(-1);
976
	btn = document.createElement("button");
977
	btn.type = "button";
978
	btn.textContent = "−";
979
	btn.disabled = (ae.admin_getUserLvl(i) === 0);
980
	btn.onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[5].textContent, 10) - 1, c);};
981
	cell.appendChild(btn);
982
983
	cell = row.insertCell(-1);
984
	btn = document.createElement("button");
985
	btn.type = "button";
986
	btn.textContent = "X";
987
	btn.onclick = function() {
988
		const tr = this.parentElement.parentElement;
989
		ae.Account_Delete(tr.cells[0].textContent, function(error) {
990
			if (error === 0) tr.remove(); else errorDialog(error);
991
		});
992
	};
993
	cell.appendChild(btn);
994
}
995
996
function updateLimits() {
997
	const tbl = document.querySelector("#tbl_limits > tbody");
998
999
	if (ae.isUserAdmin()) {
1000
		for (let i = 0; i < 4; i++) {
1001
			tbl.rows[i].cells[1].children[0].value = ae.getLimitStorage(i);
1002
			tbl.rows[i].cells[2].children[0].value = ae.getLimitNormalA(i);
1003
			tbl.rows[i].cells[3].children[0].value = ae.getLimitShieldA(i);
1004
		}
1005
	} else {
1006
		const lvl = ae.getOwnLevel();
1007
		tbl.rows[lvl].cells[1].children[0].value = ae.getLimitStorage(lvl);
1008
		tbl.rows[lvl].cells[2].children[0].value = ae.getLimitNormalA(lvl);
1009
		tbl.rows[lvl].cells[3].children[0].value = ae.getLimitShieldA(lvl);
1010
	}
1011
}
1012
1013
function deleteAddress(addr) {
1014
	const buttons = document.querySelectorAll("#tbl_addrs button");
1015
	buttons.forEach(function(btn) {btn.disabled = true;});
1016
1017
	let addressToDelete = -1;
1018
	for (let i = 0; i < ae.getAddressCount(); i++) {
1019
		if (addr === ae.getAddress(i)) {
1020
			addressToDelete = i;
1021
			break;
1022
		}
1023
	}
1024
1025
	if (addressToDelete === -1) return;
1026
1027
	ae.Address_Delete(addressToDelete, function(error1) {
1028
		if (error1 !== 0) {
1029
			buttons.forEach(function(btn) {btn.disabled = false;});
1030
			errorDialog(error1);
1031
			return;
1032
		}
1033
1034
		document.getElementById("tbl_addrs").deleteRow(addressToDelete);
1035
		document.getElementById("write_from").remove(addressToDelete);
1036
1037
		if (ae.getAddressCountNormal() > 0) {
1038
			const apkList = document.getElementById("getapk_addr");
1039
			for (let i = 0; i < apkList.children.length; i++) {
1040
				if (apkList.children[i].value === addr) {
1041
					apkList.remove(i);
1042
					break;
1043
				}
1044
			}
1045
		} else {
1046
			document.getElementById("getapk_addr").replaceChildren();
1047
			document.getElementById("btn_getapk").disabled = true;
1048
		}
1049
1050
		updateAddressCounts();
1051
1052
		ae.Private_Update(function(error2) {
1053
			buttons.forEach(function(btn) {btn.disabled = false;});
1054
			if (error2) errorDialog(error2);
1055
		});
1056
	});
1057
}
1058
1059
function clearWrite() {
1060
	setTab(false, TAB_WRITE, 0);
1061
1062
	document.querySelector("#write2_pkey > input").value = "";
1063
	document.getElementById("write_body").value = "";
1064
	document.getElementById("write_subj").value = "";
1065
	document.getElementById("write_subj").readOnly = false;
1066
	document.getElementById("write_subj").setAttribute("data-replyid", "");
1067
	document.getElementById("write_recv").value = "";
1068
	document.getElementById("write_recv").readOnly = false;
1069
	document.getElementById("write_recv").focus();
1070
}
1071
1072
function refreshContactList() {
1073
	let opts = [];
1074
1075
	for (let i = 0; i < ae.getContactCount(); i++) {
1076
		const el = document.createElement("option");
1077
		el.value = ae.getContactMail(i);
1078
		opts.push(el);
1079
	}
1080
1081
	if (ae.isUserAdmin()) {
1082
		const el = document.createElement("option");
1083
		el.value = "public";
1084
		opts.push(el);
1085
	}
1086
1087
	document.getElementById("contact_emails").replaceChildren(...opts);
1088
}
1089
1090
function addContact(mail, name, note) {
1091
	const tbl = document.getElementById("tbl_ctact");
1092
	const row = tbl.insertRow(-1);
1093
1094
	let cell = row.insertCell(-1);
1095
	cell.autocapitalize = "off";
1096
	cell.contentEditable = true;
1097
	cell.inputMode = "email";
1098
	cell.spellcheck = false;
1099
	cell.textContent = mail;
1100
1101
	cell = row.insertCell(-1);
1102
	cell.autocapitalize = "words";
1103
	cell.contentEditable = true;
1104
	cell.spellcheck = false;
1105
	cell.textContent = name;
1106
1107
	cell = row.insertCell(-1);
1108
	cell.autocapitalize = "off";
1109
	cell.contentEditable = true;
1110
	cell.spellcheck = false;
1111
	cell.textContent = note;
1112
1113
	cell = row.insertCell(-1);
1114
	const el = document.createElement("button");
1115
	el.type = "button";
1116
	el.textContent = "X";
1117
	el.onclick = function() {row.remove();};
1118
	cell.appendChild(el);
1119
}
1120
1121
function addContacts() {
1122
	for (let i = 0; i < ae.getContactCount(); i++) {
1123
		addContact(
1124
			ae.getContactMail(i),
1125
			ae.getContactName(i),
1126
			ae.getContactNote(i)
1127
		);
1128
	}
1129
1130
	refreshContactList();
1131
}
1132
1133
function addAddress(num) {
1134
	const addrTable = document.getElementById("tbl_addrs");
1135
	const row = addrTable.insertRow(-1);
1136
	const addr = ae.getAddress(num);
1137
1138
	let cell = row.insertCell(-1);
1139
	cell.textContent = addr;
1140
	cell.onclick = function() {navigator.clipboard.writeText(((this.textContent.length === 16) ? ae.shieldMix(this.textContent) : this.textContent) + "@" + ae.getDomainEml());};
1141
1142
	cell = row.insertCell(-1);
1143
	let el = document.createElement("input");
1144
	el.type = "checkbox";
1145
	el.checked = ae.getAddressAccInt(num);
1146
	cell.appendChild(el);
1147
1148
	cell = row.insertCell(-1);
1149
	el = document.createElement("input");
1150
	el.type = "checkbox";
1151
	el.checked = ae.getAddressAccExt(num);
1152
	cell.appendChild(el);
1153
1154
	cell = row.insertCell(-1);
1155
	el = document.createElement("input");
1156
	el.type = "checkbox";
1157
	el.checked = ae.getAddressAllVer(num);
1158
	cell.appendChild(el);
1159
1160
	cell = row.insertCell(-1);
1161
	el = document.createElement("input");
1162
	el.type = "checkbox";
1163
	el.checked = ae.getAddressAttach(num);
1164
	cell.appendChild(el);
1165
1166
	cell = row.insertCell(-1);
1167
	el = document.createElement("input");
1168
	el.type = "checkbox";
1169
	el.checked = ae.getAddressSecure(num);
1170
	cell.appendChild(el);
1171
1172
	cell = row.insertCell(-1);
1173
	el = document.createElement("input");
1174
	el.type = "checkbox";
1175
	el.checked = ae.getAddressOrigin(num);
1176
	cell.appendChild(el);
1177
1178
	cell = row.insertCell(-1);
1179
	el = document.createElement("button");
1180
	el.type = "button";
1181
	el.textContent = "X";
1182
	el.onclick = function() {deleteAddress(addr);};
1183
	cell.appendChild(el);
1184
1185
	el = document.createElement("option");
1186
	el.value = addr;
1187
	el.textContent = addr + "@" + ae.getDomainEml();
1188
	document.getElementById("write_from").appendChild(el);
1189
1190
	if (addr.length !== 16) {
1191
		el = document.createElement("option");
1192
		el.value = addr;
1193
		el.textContent = addr;
1194
		document.getElementById("getapk_addr").appendChild(el);
1195
	}
1196
}
1197
1198
function addAddresses() {
1199
	for (let i = 0; i < ae.getAddressCount(); i++) {
1200
		addAddress(i);
1201
	}
1202
1203
	document.getElementById("btn_getapk").disabled = (ae.getAddressCountNormal() < 1);
1204
}
1205
1206
function addressCreate(addr) {
1207
	document.getElementById("btn_address_create_normal").disabled = true;
1208
	document.getElementById("btn_address_create_shield").disabled = true;
1209
1210
	ae.Address_Create(addr, function(error1) {
1211
		if (error1 !== 0) {
1212
			updateAddressButtons();
1213
			errorDialog(error1, (addr !== "SHIELD") ? document.getElementById("txt_address_create_normal") : null);
1214
			return;
1215
		}
1216
1217
		ae.Private_Update(function(error2) {
1218
			updateAddressCounts();
1219
1220
			addAddress(ae.getAddressCount() - 1);
1221
			if (addr !== "SHIELD") {
1222
				document.getElementById("btn_getapk").disabled = false;
1223
				document.getElementById("txt_address_create_normal").value = "";
1224
				document.getElementById("txt_address_create_normal").focus();
1225
			}
1226
1227
			if (error2 !== 0) errorDialog(error2, (addr !== "SHIELD") ? document.getElementById("txt_address_create_normal") : null);
1228
		});
1229
	});
1230
}
1231
1232
function reloadAccount() {
1233
	updateLimits();
1234
	addOwnAccount();
1235
	addContacts();
1236
	addAddresses();
1237
	updateAddressCounts();
1238
1239
	document.getElementById("fs_admin").disabled = !ae.isUserAdmin();
1240
	document.getElementById("txt_notepad").value = ae.getPrivateExtra();
1241
}
1242
1243
function writeVerify() {
1244
	if (
1245
	   !document.getElementById("write_recv").reportValidity()
1246
	|| !document.getElementById("write_subj").reportValidity()
1247
	|| !document.getElementById("write_body").reportValidity()
1248
	) return false;
1249
1250
	document.getElementById("div_write_1").hidden = true;
1251
	document.getElementById("div_write_2").hidden = false;
1252
1253
	document.getElementById("write2_recv").textContent = document.getElementById("write_recv").value;
1254
	document.getElementById("write2_subj").textContent = document.getElementById("write_subj").value;
1255
	document.getElementById("write2_rply").textContent = document.getElementById("write_subj").getAttribute("data-replyid");
1256
	document.getElementById("write2_body").textContent = document.getElementById("write_body").value;
1257
1258
	if (document.getElementById("write_recv").value.indexOf("@") >= 0) {
1259
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value + "@" + ae.getDomainEml();
1260
		document.getElementById("write2_pkey").hidden = true;
1261
	} else {
1262
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value;
1263
		document.getElementById("write2_pkey").hidden = (document.getElementById("write_recv").value === "public");
1264
	}
1265
1266
	document.querySelector("#write2_send > button").disabled = false;
1267
	document.getElementById("write2_btntxt").textContent = (document.getElementById("write_recv").value === "public") ? "Make" : "Send to";
1268
	return true;
1269
}
1270
1271
function setTab(isHistory, tabNum, pageNum) {
1272
	tab = tabNum;
1273
	if (pageNum !== -1) tabs[tab].cur = pageNum;
1274
1275
	document.querySelectorAll("#main1 > nav:first-of-type > button").forEach(function(btn, i) {
1276
		document.querySelectorAll("#main1 > .mid > div")[i].hidden = (tab !== i);
1277
		btn.disabled = (tab === i);
1278
	});
1279
1280
	const bigScreen = window.matchMedia("(min-width: 80em)").matches;
1281
	document.getElementById("main2").hidden = !bigScreen;
1282
	document.getElementById("btn_leave").disabled = bigScreen;
1283
1284
	switch (tab) {
1285
		case TAB_INBOX: showInbox(); break;
1286
		case TAB_DRBOX: showDrbox(); break;
1287
1288
		case TAB_WRITE:
1289
			if (tabs[tab].cur === 0) {
1290
				document.getElementById("div_write_1").hidden = false;
1291
				document.getElementById("div_write_2").hidden = true;
1292
				document.getElementById("write_recv").focus();
1293
			} else if (!writeVerify()) {
1294
				tabs[TAB_WRITE].cur = 0;
1295
				return;
1296
			}
1297
		break;
1298
1299
		case TAB_NOTES:
1300
			switch (tabs[tab].cur) {
1301
				case 0:
1302
					document.getElementById("div_notes").children[0].hidden = false;
1303
					document.getElementById("div_notes").children[1].hidden = true;
1304
					document.getElementById("div_notes").children[2].hidden = true;
1305
				break;
1306
1307
				case 1:
1308
					document.getElementById("div_notes").children[0].hidden = true;
1309
					document.getElementById("div_notes").children[1].hidden = false;
1310
					document.getElementById("div_notes").children[2].hidden = true;
1311
1312
					document.querySelector("#div_notepad meter").value = ae.getPrivateExtraSpace() / ae.getPrivateExtraSpaceMax();
1313
				break;
1314
1315
				case 2:
1316
					document.getElementById("div_notes").children[0].hidden = true;
1317
					document.getElementById("div_notes").children[1].hidden = true;
1318
					document.getElementById("div_notes").children[2].hidden = false;
0 ignored issues
show
introduced by
This node falls through to the next case due to this statement. Please add a comment either directly below this line or between the cases to explain.
Loading history...
1319
1320
				default:
1321
					showFiles();
1322
			}
1323
		break;
1324
1325
		case TAB_TOOLS:
1326
			for (let i = 0; i <= tabs[tab].max; i++) {
1327
				document.getElementById("div_tools").children[i].hidden = (i !== tabs[tab].cur);
1328
			}
1329
		break;
1330
	}
1331
1332
	document.getElementById("btn_dele").disabled = !tabs[tab].btnDele;
1333
	document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
1334
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1335
	document.getElementById("btn_updt").disabled = !tabs[tab].btnUpdt;
1336
1337
	if (!isHistory) history.pushState({tab: tab, page: tabs[tab].cur, msg: msgDisplay}, null);
1338
}
1339
1340
// Interface elements
1341
if (window.matchMedia("(prefers-color-scheme: light)").matches) document.querySelector("head > meta[name='theme-color']").content = "#eef";
1342
window.matchMedia("(prefers-color-scheme: light)").onchange = function() {document.querySelector("head > meta[name='theme-color']").content = window.matchMedia("(prefers-color-scheme: light)").matches? "#eef" : "#001";};
1343
1344
window.onpopstate = function(event) {
1345
	if (!isReady || !event.state) return;
1346
	setTab(true, event.state.tab, event.state.page);
1347
	msgDisplay = event.state.msg;
1348
1349
	if (msgDisplay) {
1350
		switch (msgDisplay.type) {
1351
			case "ext": displayMsg(true, false, msgDisplay.num); break;
1352
			case "int": displayMsg(true, true, msgDisplay.num); break;
1353
			case "out": displayOutMsg(true, msgDisplay.num); break;
1354
			case "upl": displayFile(true, msgDisplay.num, null); break;
1355
			case "ext_exp": displayExport(true, false, msgDisplay.num); break;
1356
			case "int_exp": displayExport(true, true, msgDisplay.num); break;
1357
		}
1358
	}
1359
};
1360
1361
document.querySelectorAll("#main1 > nav:first-of-type > button").forEach(function(btn, i) {
1362
	btn.onclick = function() {setTab(false, i, -1);};
1363
});
1364
1365
document.getElementById("btn_left").onclick = function() {
1366
	setTab(false, tab, tabs[tab].cur - 1);
1367
};
1368
1369
document.getElementById("btn_rght").onclick = function() {
1370
	setTab(false, tab, tabs[tab].cur + 1);
1371
};
1372
1373
1374
document.getElementById("btn_dele").onclick = function() {
1375
	this.blur();
1376
1377
	if (tab === TAB_WRITE) clearWrite();
1378
};
1379
1380
document.getElementById("btn_updt").onclick = function() {
1381
	const btn = this;
1382
	btn.disabled = true;
1383
	btn.blur();
1384
1385
	if (tab === TAB_INBOX) {
1386
		document.getElementById("tbl_inbox").style.opacity = 0.5;
1387
1388
		ae.Message_Browse(true, false, function(error) {
1389
			btn.disabled = false;
1390
			document.getElementById("tbl_inbox").style.opacity = 1;
1391
1392
			if (error === 0) {
1393
				showInbox();
1394
			} else {
1395
				errorDialog(error);
1396
			}
1397
		});
1398
	}
1399
};
1400
1401
document.getElementById("btn_mdele").onclick = function() {
1402
	const delId = document.querySelector("article").getAttribute("data-msgid");
1403
	if (!delId) return;
1404
1405
	const btn = this;
1406
	btn.blur();
1407
	btn.disabled = true;
1408
1409
	ae.Message_Delete(delId, function(error) {
1410
		if (error !== 0) {
1411
			btn.disabled = false;
1412
			errorDialog(error);
1413
			return;
1414
		}
1415
1416
		switch (tab) {
1417
			case TAB_INBOX: showInbox(); break;
1418
			case TAB_DRBOX: showDrbox(); break;
1419
			case TAB_NOTES: showFiles(); break;
1420
		}
1421
	});
1422
};
1423
1424
document.getElementById("btn_leave").onclick = function() {
1425
	document.getElementById("main2").hidden = true;
1426
	document.getElementById("main1").hidden = false;
1427
};
1428
1429
document.getElementById("btn_newcontact").onclick = function() {
1430
	addContact("", "", "");
1431
};
1432
1433
document.getElementById("btn_savecontacts").onclick = function() {
1434
	while (ae.getContactCount() > 0) {
1435
		ae.deleteContact(0);
1436
	}
1437
1438
	for (const row of document.getElementById("tbl_ctact").rows) {
1439
		ae.addContact(row.cells[0].textContent, row.cells[1].textContent, row.cells[2].textContent);
1440
	}
1441
1442
	refreshContactList();
1443
1444
	const btn = this;
1445
	btn.disabled = true;
1446
1447
	ae.Private_Update(function(error) {
1448
		btn.disabled = false;
1449
		if (error) errorDialog(error);
1450
	});
1451
};
1452
1453
document.getElementById("btn_address_create_normal").onclick = function() {
1454
	if (ae.getAddressCountNormal() >= ae.getLimitNormalA(ae.getOwnLevel()) || ae.getAddressCountNormal() + ae.getAddressCountShield() >= 31) return;
1455
1456
	const txtNewAddr = document.getElementById("txt_address_create_normal");
1457
	if (!txtNewAddr.reportValidity()) return;
1458
1459
	addressCreate(txtNewAddr.value);
1460
};
1461
1462
document.getElementById("txt_address_create_normal").onkeyup = function(event) {
1463
	if (event.key !== "Enter") return;
1464
	event.preventDefault();
1465
	document.getElementById("btn_address_create_normal").click();
1466
};
1467
1468
document.getElementById("btn_address_create_shield").onclick = function() {
1469
	if (ae.getAddressCountShield() >= ae.getLimitShieldA(ae.getOwnLevel()) || ae.getAddressCountNormal() + ae.getAddressCountShield() >= 31) return;
1470
1471
	addressCreate("SHIELD");
1472
};
1473
1474
document.getElementById("btn_address_update").onclick = function() {
1475
	const btn = this;
1476
	btn.disabled = true;
1477
1478
	const rows = document.getElementById("tbl_addrs").rows;
1479
1480
	for (let i = 0; i < rows.length; i++) {
1481
		ae.setAddressAccInt(i, rows[i].getElementsByTagName("input")[0].checked);
1482
		ae.setAddressAccExt(i, rows[i].getElementsByTagName("input")[1].checked);
1483
		ae.setAddressAllVer(i, rows[i].getElementsByTagName("input")[2].checked);
1484
		ae.setAddressAttach(i, rows[i].getElementsByTagName("input")[3].checked);
1485
		ae.setAddressSecure(i, rows[i].getElementsByTagName("input")[4].checked);
1486
		ae.setAddressOrigin(i, rows[i].getElementsByTagName("input")[5].checked);
1487
	}
1488
1489
	ae.Address_Update(function(error) {
1490
		btn.disabled = false;
1491
		if (error) errorDialog(error);
1492
	});
1493
};
1494
1495
1496
document.getElementById("txt_reg").onkeyup = function(event) {
1497
	if (event.key !== "Enter") return;
1498
	event.preventDefault();
1499
	document.getElementById("btn_reg").click();
1500
};
1501
1502
document.getElementById("btn_reg").onclick = function() {
1503
	const btn = document.getElementById("btn_reg");
1504
	const txt = document.getElementById("txt_reg");
1505
	if (!txt.reportValidity()) return;
1506
	btn.disabled = true;
1507
1508
	ae.Account_Create(txt.value, function(error) {
1509
		if (error === 0) {
1510
			addAccountToTable(ae.admin_getUserNum() - 1);
1511
			txt.value = "";
1512
		} else errorDialog(error);
1513
1514
		btn.disabled = false;
1515
	});
1516
};
1517
1518
document.getElementById("chk_dng_usr").onclick = function() {
1519
	document.getElementById("btn_lowme").disabled = !this.checked || (ae.getOwnLevel() === 0);
1520
	document.getElementById("btn_erame").disabled = !this.checked;
1521
	document.getElementById("btn_delme").disabled = !this.checked;
1522
};
1523
1524
document.getElementById("btn_erame").onclick = function() {
1525
	ae.Message_Delete("ALL", function(error) {
1526
		if (error === 0) {
1527
			document.getElementById("chk_dng_usr").checked = false;
1528
			document.getElementById("chk_dng_usr").onclick();
1529
		} else errorDialog(error);
1530
	});
1531
};
1532
1533
document.getElementById("btn_notepad_restore").onclick = function() {
1534
	document.getElementById("txt_notepad").value = ae.getPrivateExtra();
1535
	document.getElementById("btn_notepad_savepad").disabled = true;
1536
	document.getElementById("txt_notepad").oninput = function() {
1537
		this.oninput = null;
1538
		document.getElementById("btn_notepad_savepad").disabled = false;
1539
		document.getElementById("btn_notepad_savepad").textContent = "Save";
1540
	};
1541
};
1542
1543
document.getElementById("txt_notepad").oninput = function() {
1544
	document.getElementById("btn_notepad_savepad").disabled = false;
1545
};
1546
1547
document.getElementById("btn_notepad_savepad").onclick = function() {
1548
	const btn = this;
1549
	btn.disabled = true;
1550
1551
	const error = ae.setPrivateExtra(document.getElementById("txt_notepad").value);
1552
	if (error !== 0) {
1553
		btn.disabled = false;
1554
		errorDialog(error);
1555
		return;
1556
	}
1557
1558
	ae.Private_Update(function(error2) {
1559
		if (error2 !== 0) {
1560
			btn.disabled = false;
1561
			errorDialog(error2);
1562
		} else {
1563
			document.querySelector("#div_notepad meter").value = ae.getPrivateExtraSpace() / ae.getPrivateExtraSpaceMax();
1564
			btn.textContent = "Saved";
1565
			document.getElementById("txt_notepad").oninput = function() {
1566
				this.oninput = null;
1567
				btn.textContent = "Save";
1568
				btn.disabled = false;
1569
			};
1570
		}
1571
	});
1572
};
1573
1574
document.getElementById("btn_notepad_saveupl").onclick = function() {
1575
	const np = document.getElementById("txt_notepad");
1576
	np.disabled = true;
1577
1578
	let fname = prompt("Save as...", "Untitled");
0 ignored issues
show
Debugging Code Best Practice introduced by
The prompt UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
1579
	if (!fname.endsWith(".txt")) fname += ".txt";
1580
1581
	ae.Message_Upload(fname, np.value, function(error) {
1582
		if (error === 0) {
1583
			np.value = "";
1584
			showFiles();
1585
			document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.getTotalMsgBytes() / 1024 / 1024);
1586
		} else errorDialog(error);
1587
1588
		np.disabled = false;
1589
	});
1590
};
1591
1592
document.getElementById("btn_upload").onclick = function() {
1593
	const btn = this;
1594
	const fileSelector = document.createElement("input");
1595
	fileSelector.type = "file";
1596
	fileSelector.click();
1597
1598
	fileSelector.onchange = function() {
1599
		btn.disabled = true;
1600
1601
		const reader = new FileReader();
1602
		reader.onload = function() {
1603
			if (vaultOk) { // Vault opened -> upload to PostVault
1604
				vault.uploadFile(fileSelector.files[0].name, new Uint8Array(reader.result), function(status) {
1605
					if (status === 0) {
1606
						showFiles();
1607
					} // TODO else show error
1608
1609
					btn.disabled = false;
1610
				});
1611
			} else { // No vault access -> upload to All-Ears
1612
				ae.Message_Upload(fileSelector.files[0].name, new Uint8Array(reader.result), function(status) {
1613
					if (status === 0) {
1614
						showFiles();
1615
						document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.getTotalMsgBytes() / 1024 / 1024);
1616
					} else errorDialog(error);
0 ignored issues
show
Bug introduced by
The variable error seems to be never declared. If this is a global, consider adding a /** global: error */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1617
1618
					btn.disabled = false;
1619
				});
1620
			}
1621
		};
1622
1623
		reader.readAsArrayBuffer(fileSelector.files[0]);
1624
	};
1625
};
1626
1627
document.getElementById("btn_pg").onclick = function() {
1628
//	localStorage.greeting = document.getElementById("txt_pg").value;
1629
};
1630
1631
document.querySelector("#write2_send > button").onclick = function() {
1632
	const btn = this;
1633
	btn.disabled = true;
1634
1635
	// Public announcement
1636
	if (document.getElementById("write2_recv").textContent === "public") {
1637
		ae.Message_Public(document.getElementById("write_subj").value, document.getElementById("write_body").value, function(error) {
1638
			if (error !== 0) {
1639
				document.getElementById("write2_btntxt").textContent = "Retry making";
1640
				btn.disabled = false;
1641
				errorDialog(error);
1642
				return;
1643
			}
1644
1645
			clearWrite();
1646
			displayMsg(false, true, 0);
1647
		});
1648
1649
		return;
1650
	}
1651
1652
	// Email or internal message
1653
	let apk = null;
1654
	if (document.getElementById("write2_recv").textContent.indexOf("@") === -1) {
1655
		const elApk = document.querySelector("#write2_pkey > input");
1656
		if (!elApk.reportValidity()) {
1657
			btn.disabled = false;
1658
			return;
1659
		}
1660
1661
		try {apk = sodium.from_base64(elApk.value, sodium.base64_variants.ORIGINAL_NO_PADDING);}
1662
		catch(e) {
1663
			errorDialog(1); // Invalid input
1664
			btn.disabled = false;
1665
			return;
1666
		}
1667
	}
1668
1669
	document.getElementById("write2_btntxt").textContent = "Sending to";
1670
1671
	ae.Message_Create(
1672
		document.getElementById("write_subj").value,
1673
		document.getElementById("write_body").value,
1674
		document.getElementById("write_from").value,
1675
		document.getElementById("write_recv").value,
1676
		document.getElementById("write_subj").getAttribute("data-replyid"),
1677
		apk,
1678
		function(error) {
1679
			if (error !== 0) {
1680
				errorDialog(error);
1681
				document.getElementById("write2_btntxt").textContent = "Retry sending to";
1682
				btn.disabled = false;
1683
				return;
1684
			}
1685
1686
			showDrbox();
1687
			clearWrite();
1688
			displayOutMsg(false, 0);
1689
		}
1690
	);
1691
};
1692
1693
document.getElementById("btn_sender").onclick = function() {
1694
	ae.Message_Sender(document.getElementById("txt_sender_hash").value, Date.parse(document.getElementById("txt_sender_date").value) / 1000, function(error, result) {
1695
		if (error !== 0) {
1696
			errorDialog(error);
1697
			return;
1698
		}
1699
1700
		document.getElementById("txt_sender_res").value = result;
1701
	});
1702
};
1703
1704
document.getElementById("btn_limits").onclick = function() {
1705
	const btn = this;
1706
	btn.disabled = true;
1707
1708
	const mib = [parseInt(document.getElementById("lim_mib0").value, 10), parseInt(document.getElementById("lim_mib1").value, 10), parseInt(document.getElementById("lim_mib2").value, 10), parseInt(document.getElementById("lim_mib3").value, 10)];
1709
	const nrm = [parseInt(document.getElementById("lim_nrm0").value, 10), parseInt(document.getElementById("lim_nrm1").value, 10), parseInt(document.getElementById("lim_nrm2").value, 10), parseInt(document.getElementById("lim_nrm3").value, 10)];
1710
	const shd = [parseInt(document.getElementById("lim_shd0").value, 10), parseInt(document.getElementById("lim_shd1").value, 10), parseInt(document.getElementById("lim_shd2").value, 10), parseInt(document.getElementById("lim_shd3").value, 10)];
1711
1712
	ae.Setting_Update(mib, nrm, shd, function(error) {
1713
		btn.disabled = false;
1714
1715
		if (error !== 0) {
1716
			errorDialog(error);
1717
		} else {
1718
			updateAddressCounts();
1719
		}
1720
	});
1721
};
1722
1723
document.getElementById("btn_getapk").onclick = function() {
1724
	document.getElementById("getapk_result").textContent = ae.getOwnApk(document.getElementById("getapk_addr").value);
1725
};
1726
1727
document.getElementById("txt_skey").onfocus = function() {
1728
//	document.getElementById("greeting").textContent = localStorage.greeting;
1729
};
1730
1731
document.getElementById("txt_skey").onkeyup = function(event) {
1732
	if (event.key !== "Enter") return;
1733
	event.preventDefault();
1734
	document.getElementById("btn_enter").click();
1735
};
1736
1737
document.getElementById("btn_enter").onclick = function() {
1738
	const txtSkey = document.getElementById("txt_skey");
1739
1740
	if (txtSkey.value === "") {
1741
		ae.reset();
1742
		document.getElementById("greeting").textContent = "Data cleared";
1743
		return;
1744
	}
1745
1746
	if (!txtSkey.reportValidity()) return;
1747
1748
	const btn = this;
1749
	btn.disabled = true;
1750
1751
	document.getElementById("txt_skey").disabled = true;
1752
1753
	ae.setKeys(txtSkey.value, function(successSetKeys) {
1754
		if (!successSetKeys) {
1755
			document.getElementById("txt_skey").disabled = false;
1756
			txtSkey.focus();
1757
1758
			document.getElementById("greeting").textContent = "SetKeys failed";
1759
			btn.disabled = false;
1760
			return;
1761
		}
1762
1763
		if (vaultOk === false) {
1764
			vault.setKeys(txtSkey.value, function(successPv) {
1765
				if (!successPv) vaultOk = null;
1766
			});
1767
		}
1768
1769
		document.body.style.cursor = "wait";
1770
		document.getElementById("greeting").textContent = "Connecting...";
1771
1772
		ae.Message_Browse(false, true, function(statusBrowse) {
1773
			document.body.style.cursor = "";
1774
1775
			if (statusBrowse !== 0 && statusBrowse !== 0x09) {
1776
				document.getElementById("greeting").textContent = ae.getErrorMessage(statusBrowse) + " (0x" + statusBrowse.toString(16).padStart(2, "0").toUpperCase() + ")";
1777
				document.getElementById("txt_skey").disabled = false;
1778
				btn.disabled = false;
1779
				btn.focus();
1780
				return;
1781
			}
1782
1783
			txtSkey.value = "";
1784
			document.getElementById("div_begin").hidden = true;
1785
			document.getElementById("div_main").hidden = false;
1786
			isReady = true;
1787
			reloadAccount();
1788
			history.replaceState({tab: 0, page: 0, msg: msgDisplay}, null);
1789
			setTab(true, 0, 0);
1790
1791
			if (statusBrowse !== 0) errorDialog(statusBrowse);
1792
			if (!ae.isUserAdmin()) return;
1793
1794
			ae.Account_Browse(function(statusAcc) {
1795
				if (statusAcc === 0) {
1796
					for (let i = 0; i < ae.admin_getUserNum(); i++) {addAccountToTable(i);}
1797
					updateLimits();
1798
				} else {
1799
					errorDialog(statusAcc);
1800
				}
1801
			});
1802
		});
1803
	});
1804
};
1805
1806
});
1807